﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace COMP376A3_9609695
{
    class Ball : Drawable, Updates
    {
        private Matrix ballModelMat;
        private Model ballModel;
        private BoundingSphere bounds;
        private Vector3 position;
        public float X { get { return position.X; } set { position.X = value; } }
        public float Y { get { return position.Y; } set { position.Y = value; } }
        public float Z { get { return position.Z; } set { position.Z = value; } }
        private Vector3 offset;
        private float yaw, pitch, roll;
        private Vector3 angleUp, angleRight, angleForward;
        private Matrix rotation;
        private Vector3 momentum;
        private float maxmX;
        public float maxmZ;
        float xBoundary;
        private float jumpH, jumpMax, jumpAcc;
        private float yMin;
        public float speed;
        public float radius;
        public bool landed { get; private set; }
        private bool falling;
        public Sparks sparks;
 
        public Ball(Model model)
        {
            ballModel = model;
            offset = model.Meshes.First().BoundingSphere.Center;
            ballModelMat = Matrix.CreateTranslation(offset * -1f);
            ballModelMat *= Matrix.CreateTranslation(position);
            bounds = model.Meshes.First().BoundingSphere;
            position = new Vector3(0, 0, 0);
            momentum = new Vector3();
            yaw = pitch = roll = 0;
            angleRight = new Vector3(1, 0, 0);
            angleForward = new Vector3(0, 0, 1);
            rotation = Matrix.Identity;
            speed = 3f; // Degrees / s
            radius = 1.0f;
            maxmX = 100;
            maxmZ = 100 + (float)Math.Log(Z + 1, Math.E * 4 / 5) * (float)Math.Log(Z + 1) * 2; ;
            xBoundary = (float)Math.Sqrt(0.75) * 6;
            jumpH = 0;
            jumpMax = 0.5f;
            jumpAcc = 0.28f;
            landed = true;
            yMin = -1.0f;
            falling = false;
        }

        public void move(float x, float y, float z)
        {
            Vector3 newMomentum = new Vector3(x * (1+maxmZ/300) , y, -z);
            momentum += newMomentum;
        }

        public void jump()
        {
            if (landed)
            {
                jumpMax = 0.75f;
                jumpH = jumpMax;
                landed = false;
            }
        }

        public void bounce()
        {
            jumpMax /= 3;
            jumpH = jumpMax;
            landed = false;
        }

        public void Update(GameTime time)
        {
            int elapsed = time.ElapsedGameTime.Milliseconds + 1;

            float mX = momentum.X;
            float mZ = momentum.Z;
            if (jumpH <= 0 && !falling)
            {
                jumpH = 0;
                Y = yMin;
            }
            else
            {
                jumpH -= jumpAcc / elapsed;
                Y += jumpH - jumpMax/2;
            }
            if (Y <= yMin)
            {
                Y = yMin;
                landed = true;
                if (jumpMax >= 0.4)
                    sparks = new Sparks(X, Y+1, Z, 50);
                if (jumpMax >= 0.1f)
                    bounce();
            }

            if (!falling)
            {
                float ballHalfRad = (float)Math.Sqrt(0.5f * (2*radius - 0.5f));
                if (X < -6.0f + ballHalfRad)
                {
                    if (X > -6.0f || Math.Abs(X - 6.0f) < 0.0001)
                    {
                        yMin = (float)Math.Sqrt(radius*radius - (X + 6.0f) * (X + 6.0f)) - 1.5f;
                        if (Y <= yMin)
                        {
                            if (mX < 0)
                                mX *= -0.5f;
                            else
                                mX = (1 - (float)Math.Abs(X + 6.0f)) * maxmX / 4;
                        }
                    }
                    else
                    {
                        if (X > -7.0f)
                            yMin = -0.5f;
                        else
                        {
                            if (X > -7.0f - ballHalfRad)
                                yMin = (float)Math.Sqrt(1 - (X + 7.0f) * (X + 7.0f)) - 1.5f;
                            else
                            {
                                plummet();
                            }
                        }
                    }

                }
                else if (X > 6.0f - ballHalfRad)
                {
                    if (X <= 6.0f)
                    {
                        yMin = (float)Math.Sqrt(1 - (X - 6.0f) * (X - 6.0f)) - 1.5f;
                        if (Y <= yMin)
                        {
                            if (mX > 0)
                                mX *= -0.5f;
                            else
                                mX = -(1 - (float)Math.Abs(X - 6.0f)) * maxmX / 4;
                        }
                    }
                    else
                    {
                        if (X < 7.0f)
                            yMin = -radius/2f;
                        else
                        {
                            if (X < 7.0f + ballHalfRad)
                                yMin = (float)Math.Sqrt(1 - (X - 7.0f) * (X - 7.0f)) - 1.5f;
                            else
                            {
                                plummet();
                            }
                        }
                    }

                }
                else
                    yMin = -1.0f;

                if (Y <= yMin)
                    Y = yMin;
            }

            if (mX > maxmX)
                mX = maxmX;
            if (mX < -maxmX)
                mX = -maxmX;
            float rZ = mX * speed / elapsed;
            rZ = rZ * MathHelper.Pi / 180;
            float tX = rZ * radius;

            if (mZ > maxmZ)
                mZ = maxmZ;
            if (mZ < -maxmZ)
                mZ = -maxmZ;
            float rX = mZ * speed / elapsed;
            rX = rX * MathHelper.Pi / 180;
            float tZ = rX * radius;

            position.X += tX;
            position.Z += tZ;

            rotation *= Matrix.CreateRotationX(rX);
            rotation *= Matrix.CreateRotationZ(-rZ);

            ballModelMat = Matrix.CreateScale(2*radius-radius) * Matrix.CreateTranslation(offset * -radius);

            ballModelMat = ballModelMat * rotation * Matrix.CreateTranslation(position.X, position.Y + radius, position.Z);

            if (mX > 0)
            {
                mX -= 1f / ((float)elapsed / 5);
                if (mX < 0)
                    mX = 0;
            }
            if (mX < 0)
            {
                mX += 1f / ((float)elapsed / 5);
                if (mX > 0)
                    mX = 0;
            }
            momentum.X = mX;

            if (mZ > 0)
            {
                mZ -= 1f / ((float)elapsed / 5);
                if (mZ < 0)
                    mZ = 0;
            }
            if (mZ < 0)
            {
                mZ += 1f / ((float)elapsed / 5);
                if (mZ > 0)
                    mZ = 0;
            }
            momentum.Z = mZ;

            if (maxmZ < 300)
                maxmZ = 100 + (float)Math.Log(Z + 1, Math.E * 4 / 5) * (float)Math.Log(Z + 1) * 2;
            else
                maxmZ = 300;
            if (sparks != null && sparks.getDots().Count > 0)
            {
                sparks.Update(time);
            }
        }

        public Model getModel()
        {
            return ballModel;
        }

        public Matrix getModelMatrix()
        {
            return ballModelMat;
        }

        public void bounceX()
        {
            momentum.X *= -0.5f;
        }

        public void plummet()
        {
            yMin = -300;
            jumpMax = -0.1f;
            jumpH = -0.1f;
            falling = true;
        }
    }
}
